package session

import (
	"context"
	"encoding/gob"
	"fmt"
	"net"
	"src/config"
	"src/iinf"
	"src/log"
	"src/message"
	"src/sessionadmin"
)

//ServerSession 管理的设备端连接会话
type ServerSession struct {
	iinf.SessionBase
	status           string
	connProxy        net.Conn
	id               int
	desc             string
	listenPortClient int
	listenPortDev    int
	ctx              context.Context
	cancelFunc       context.CancelFunc
	writech          chan *message.Message
	listenerClient   net.Listener
	listenerDev      net.Listener
	decoder          *gob.Decoder
	encoder          *gob.Encoder
	admin            *sessionadmin.ClientAdmin
}

//NetCmdProc NetCmdProc
func (ses *ServerSession) NetCmdProc(msg *message.Message) bool {
	switch msg.Cmd {
	case message.MsgKeepalive:
	case message.MsgHello:
		ses.desc = msg.Data
		log.Println("get session id=", ses.id, "desc=", ses.desc)
	default:
		log.Println("err cmd=", msg.Cmd)
	}

	return true
}

//notifyDevServer 通知设备来连接服务器做中转了
func (ses *ServerSession) notifyDevServer() {
	msg := new(message.Message)
	log.Println("goto notify dev connect here!!!!!!!!!!!!!")
	server := fmt.Sprintf("%s:%d", config.ServerListenAddr, ses.listenPortDev)
	msg.Cmd = message.MsgDevServerNotify
	msg.Data = server
	ses.send2Dev(msg)
}

func (ses *ServerSession) initListen() bool {
	//监听连接客户端
	ln, err := net.Listen("tcp", fmt.Sprintf(":%d", ses.listenPortClient))
	if err != nil {
		log.Println("listen err=", err)
		return false
	}
	ses.listenerClient = ln
	go func(ses *ServerSession) {
		defer ses.setQuit()
		log.Println("ses id=", ses.id, "goto listening by listenPortClient=", ses.listenPortClient)
		for {
			select {
			case <-ses.ctx.Done():
				log.Println("listenerClient ctx done exit now ")
				return
			default:
				conn, err := ses.listenerClient.Accept()
				if err != nil {
					log.Println("clientconn这个错误比较严重，直接挂了重来算了")
					log.Println(err)
					return
				}

				//判断一下忙不忙
				if ses.admin.IsBusy() {
					log.Println("session admin is busy wait a moment")
					conn.Close()
					continue
				}

				log.Println("listenPortClient recv client conn from ", conn.RemoteAddr().String())
				//创建传话并通知设备端连接过来
				id := ses.admin.GetID()
				if id < 0 {
					log.Println("session is too big now")
					conn.Close()
					continue
				}
				client := GetNewClientSession(id, conn)
				ses.admin.AddSession(client)
				//通知设备端
				ses.notifyDevServer()
			}
		}
	}(ses)
	//监听设备连接端
	ln, err = net.Listen("tcp", fmt.Sprintf(":%d", ses.listenPortDev))
	if err != nil {
		log.Println("listen err=", err)
		return false
	}
	ses.listenerDev = ln
	go func(ses *ServerSession) {
		defer ses.setQuit()
		log.Println("ses id=", ses.id, "goto listening by listenPortDev=", ses.listenPortDev)
		for {
			select {
			case <-ses.ctx.Done():
				log.Println("listenerDev ctx done exit")
				return
			default:
				conn, err := ses.listenerDev.Accept()
				if err != nil {
					//这个错误比较严重，直接挂了重来算了
					log.Println("devconn 这个错误比较严重，直接挂了重来算了")
					log.Println(err)
					return
				}

				log.Println("listenPortDev recv dev conn=", conn)
				//绑定并开启中转
				ses.admin.AddDevConn(conn)
			}
		}
	}(ses)

	return true
}

//Read Read
func (ses *ServerSession) read(msg *message.Message) error {
	return ses.decoder.Decode(msg)
}

//Write Write
func (ses *ServerSession) write(msg *message.Message) error {
	return ses.encoder.Encode(msg)
}

// Close Close
func (ses *ServerSession) Close() {
	ses.setQuit()
	ses.cancelFunc()

	if ses.connProxy != nil {
		ses.connProxy.Close()
		ses.connProxy = nil
	}
	if ses.listenerClient != nil {
		ses.listenerClient.Close()
		ses.listenerClient = nil
	}
	if ses.listenerDev != nil {
		ses.listenerDev.Close()
		ses.listenerDev = nil
	}

	close(ses.writech)
	ses.admin.Close()
	ses.encoder = nil
	ses.decoder = nil
}

//SyncSession 删除删除退出的会话
func (ses *ServerSession) SyncSession() {
	ses.admin.SyncSession()
}

//GetID 获取当前会话的id
func (ses *ServerSession) GetID() int {
	// log.Println("serversession id=", ses.id)
	return ses.id
}

//IsQuit 当前会话是否已退出状态
func (ses *ServerSession) IsQuit() bool {
	if ses.status == "quit" {
		return true
	}

	return false
}

//Show 显示当前会话信息
func (ses *ServerSession) Show() {
	log.Println(ses.GetInfo())
}

//GetInfo GetInfo
func (ses *ServerSession) GetInfo() string {
	str := fmt.Sprintf("session id=%d listenPortClient=%d listenPortDev=%d desc=%s\r\n",
		ses.id, ses.listenPortClient, ses.listenPortDev, ses.desc)
	str += ses.admin.GetInfo()

	return str
}

func (ses *ServerSession) send2Dev(msg *message.Message) {
	ses.writech <- msg
}

func (ses *ServerSession) setRunning() {
	ses.status = "running"
}

func (ses *ServerSession) setQuit() {
	ses.status = "quit"
}

func (ses *ServerSession) setListenning() {
	ses.status = "listen"
}

//ConnStart 初始化一个协程用来给会话写通知命令
func (ses *ServerSession) ConnStart() {
	//初始化一个协程监听写信道并写数据
	go func(ses *ServerSession) {
		defer ses.setQuit()
		for {
			select {
			case <-ses.ctx.Done():
				log.Println("ConnStart ctx done exit")
				return
			case msg, ok := <-ses.writech:
				if !ok {
					log.Println("writech read failed quit now")
					return
				}
				log.Println("write data to conn")
				err := ses.write(msg)
				if err != nil {
					log.Println("conn encoder write failed")
					return
				}
			}
		}
	}(ses)

	ses.setRunning()
	//读会话客户端数据
	for {
		msg := new(message.Message)
		err := ses.read(msg)
		if err != nil {
			log.Println("recv and err=", err)
			break
		}
		if !ses.NetCmdProc(msg) {
			break
		}
	}

	ses.setQuit()
	log.Println("session ses_id=", ses.id, "is quit now!!!!!!!!!!!!!")
}

// GetNewServerSession GetNewServerSession
func GetNewServerSession(conn net.Conn, sessionID, clientPort, devPort int) *ServerSession {
	ses := &ServerSession{
		id:               sessionID,
		connProxy:        conn,
		listenPortClient: clientPort,
		listenPortDev:    devPort,
		admin:            sessionadmin.GetNewClientAdmin(),
		writech:          make(chan *message.Message, 3),
		status:           "idle",
	}
	ses.encoder = gob.NewEncoder(conn)
	ses.decoder = gob.NewDecoder(conn)
	ses.ctx, ses.cancelFunc = context.WithCancel(context.Background())
	if !ses.initListen() {
		log.Println("ses id=", ses.id, "init listen failed listenPortClient=", ses.listenPortClient,
			"listenPortDev=", ses.listenPortDev)
		ses.Close()
		return nil
	}

	ses.setListenning()

	return ses
}
